/*
Cache.cpp
*/

#include "Cache.h"
#include "FileSystem.h"

Cache::Cache(FileSystem * fs_loc):
    fs(fs_loc)
{
    //calculate how many blocks we can hold in the cache
    max_map_size = 10*1024*1024/Settings::BlockSize;
}

/*
Frees memory
*/
Cache::~Cache(){
    
    map <unsigned int, DataBlock *>::iterator it;
    DataBlock * data_block;

    for(it = blocks.begin(); it != blocks.end(); it++){
        data_block = it->second;
        delete data_block;
    }
    
}

/*
this is not finished, it needs to implement LRU for when the cache is full
*/
DataBlock * Cache::GetDataBlock(unsigned int block_id){
    
    DataBlock * data_block;
    map <unsigned int, DataBlock *>::iterator it; //index = block_id
    list < DataBlock* >::iterator lru_it;

    it = blocks.find(block_id);
    if(it != blocks.end()){
        //block was in cache
        data_block = it->second;

        //move it to the back of the lru list
        lru_list.erase(data_block->lru_it);
        lru_list.push_back(data_block);
        data_block->lru_it = lru_list.end();
        data_block->lru_it--;

        return data_block;
    }

    //block is not in the cache

    if(blocks.size() >= max_map_size){
        //no more space in cache for this new block, get rid of the least recently used block

        lru_it = lru_list.begin();
        data_block = *lru_it;

        //remove it from the lru list
        lru_list.erase(lru_it);

        //remove it from the cache
        blocks.erase(data_block->id);

        //free memory
        delete data_block;
    }

    //block is not in cache, read it from the file
    data_block = new DataBlock(block_id,fs);

    //add it to the cache
    blocks.insert(make_pair(block_id,data_block));

    //put the block in the back of the lru list
    lru_list.push_back(data_block);
    data_block->lru_it = lru_list.end();
    data_block->lru_it--;

    return data_block;

}
